package mdr;


import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.TreeMap;
import ReadWGZ.ReadFilewgzMDR;
import model.CUT;
import model.Filter;
import model.MethodDeclared;
import model.StatementKind;
import model.TestClusterBuilder;
import ga.GAInfo;
import ga.Individual;
import instrumentation.BlockClass;
import instrumentation.BlockMethodEntry;
import instrumentation.Condition;
import instrumentation.Data;
import instrumentation.DataUse;
import instrumentation.DataDef;
import instrumentation.Value;
import instrumentation.Virtualinvoke;
import instrumentation.ValueArray;
import instrumentation.Constant;
import instrumentation.ConstantString;
import instrumentation.ConditionIf;
import instrumentation.ConditionSwitch;
public class CopyOfRecommendGenerator11_18_2014 {
	private  final GAInfo gaInfo;
	private final Map<Integer, Map<Class<?>,  List<StatementKind>>>   branchRelStatementList;//relMethodList
	private final Map<Integer, Condition>   condition;//relMethodList
	private final Map<Integer, Map<Integer, Value>>   branchWithData;//branch with d1 and d2
	private final Map<Integer, BlockMethodEntry>   branchMethod;//relMethodList

	public CopyOfRecommendGenerator11_18_2014(GAInfo gaInfo){
		this.gaInfo=gaInfo;
		condition=new LinkedHashMap<Integer, Condition> ();
		branchWithData=new LinkedHashMap<Integer, Map<Integer, Value>>() ;
		branchMethod=new LinkedHashMap<Integer, BlockMethodEntry> ();

		branchRelStatementList=this.getRecommendMethods();
	}
	public  List<StatementKind> getRecommendMethodsBranch(int trgtBR){
		List<StatementKind> relatedMethodsList= new LinkedList<StatementKind>();
		 Map<Class<?>,  List<StatementKind>> relatedMethodBranch= this.branchRelStatementList.get(trgtBR);
		 for(Class<?> clz:relatedMethodBranch.keySet())
			 relatedMethodsList.addAll(relatedMethodBranch.get(clz));
		 
		 return relatedMethodsList;
		 
	}
	public  Map<Integer, Value> getBranchWithData(int trgtBR){
		return this.branchWithData.get(trgtBR);
	}
	public  Condition getCondition(int trgtBR){
		return this.condition.get(trgtBR);
	}
	public  BlockMethodEntry getTrgtMethod(int trgtBR){
		return this.branchMethod.get(trgtBR);
	}

	public  Set<Integer> getAllBranchesClz(){
		return new HashSet<Integer>(condition.keySet());
	}

	public void displayCondition(int trgtBR, Individual individual,Map<Class<?>,  List<StatementKind>> recommendList){
//		System.out.println(individual);
		System.out.println("-------------------------");
		System.out.println(condition.get(trgtBR).toString());
		System.out.println("-------------------------");
		System.out.println(this.branchWithData.get(trgtBR));
		System.out.println("-------------------------");
		System.out.println(recommendList);

	}
	
	private Map<Integer, Map<Class<?>,  List<StatementKind>>>  getRecommendMethods( ){ 

		Map<Integer, Map<Class<?>,  List<StatementKind>>> branchStatementDependences =new LinkedHashMap<Integer, Map<Class<?>, List<StatementKind>>>();
		TestClusterBuilder tCluster=this.gaInfo.getTestCluster();
		Map<String, BlockClass>  mapBlckClazz=gaInfo.getmapBlockClass();
//		BlockClass bc = (BlockClass) mapBlckClazz.get(cut);
		for (Entry<String, BlockClass> block : mapBlckClazz.entrySet()) {
			BlockClass blckClazz = block.getValue();
			if(!blckClazz.getClzName().equals(tCluster.getCut().getClazz().getName()))continue;
			Set<BlockMethodEntry> blckMethd = blckClazz.getBlockMethods();
			for (BlockMethodEntry blockkMethod : blckMethd) {
				Set<Condition> methodConditions = blockkMethod.getCondition();
				for (Condition c : methodConditions){
					 Map<Integer, Condition> conditions=new LinkedHashMap<Integer, Condition>();

					 Map<Class<?>,  List<StatementKind>> statementDependences =new LinkedHashMap <Class<?>, List<StatementKind>>();
					 if(c instanceof ConditionIf){
						 branchMethod.put(c.getBranchId(), blockkMethod);
						 conditions.put(c.getBranchId(), c);
					 }
					 else{
						 if(c instanceof ConditionSwitch){
								Map<Integer,Integer> cases=((ConditionSwitch)c).getCases();
								for(Integer caseID:cases.keySet()){
									branchMethod.put(caseID, blockkMethod);
									conditions.put(caseID, c);
								}
								Integer defaultCase=((ConditionSwitch)c).getDefaultCase();
								branchMethod.put(defaultCase, blockkMethod);
								conditions.put(defaultCase, c);

						 }
					 }
						String clz=blckClazz.getClzName();
						String method=blockkMethod.getFullQualifiedName();

					for(Integer trgtBR:conditions.keySet()){
						condition.put(trgtBR, c);
	//					System.out.println("trgtBR : " + trgtBR +   c.toString());
						if(trgtBR==96)
							System.out.println();
						Map<Integer, Value> predicateBR= new LinkedHashMap<Integer, Value>() ;
	//					if(c.getMethodCall()!=null)continue;
							for(int i=1; i<=2;i++){
								Value v= this.getDataCondition(trgtBR,c,i);
								if(v==null)continue;
								predicateBR.put(i, v);
								if(v instanceof Virtualinvoke ){
									Virtualinvoke virtual=((Virtualinvoke) v);
									v=virtual.getD();

//									if(virtual.getMethodRef().equals("instanceof"))
//										if(virtual.getArgs().size()!=0 )
//											v=virtual.getArgs().get(0);
									
								}
								if(v instanceof ValueArray ) v=((ValueArray) v).getD();
								if(!(v instanceof Data)) continue; 
								
								Data d=(Data)v;
								if(d.isField()||d.isParam())
									predicateBR.put(i, v);

			//					System.out.println("\t "+ clz + " "+ method );
								if(d.isField()){
			//						System.out.println("\t v1 is a Field"+ c.toString() );
									statementDependences.putAll(Condition_isField(d,clz,method));
									
								}
								else if(d.isParam()){
			//						System.out.println("\t v1 is either para or NA "+ c.toString() );
									if(blockkMethod.isPrivate()){
			//							System.out.println(" \t method is private" );	
										statementDependences.putAll(Condition_private_method(clz,method));
									}else{
			//							System.out.println("\t method is public or constructor" );	
										statementDependences.putAll(Condition_public_method_ParamPredicate(clz,method));
							//			statementDependences.putAll(Condition_public_method(clz,method));

									}
								}
			//					System.out.println(" \t related method:" + statementDependences );
							}

					if(statementDependences.size()==0){
						if(method.contains("<init>")){
							statementDependences.putAll(Condition_All_Write_method(clz,method));
						}
						else if(blockkMethod.isPrivate()){
							//							System.out.println(" \t method is private" );	
								statementDependences.putAll(Condition_private_method(clz,method));
							}else{
							//							System.out.println("\t method is public or constructor" );	
								statementDependences.putAll(Condition_public_method(clz,method));
							}
					}
//					System.out.println(" \t related method:" + statementDependences );

					branchStatementDependences.put(trgtBR,statementDependences);
					this.branchWithData.put(trgtBR, predicateBR);
				}
				}
			}
		}
			return branchStatementDependences;
		}
	private  Map<Class<?>,  List<StatementKind>> Condition_All_Write_method(String clz,String method){
		Map<Class<?>,  List<StatementKind>> statementDependences =new LinkedHashMap<Class<?>, List<StatementKind>>();
		 Map<Class<?>, Map<String, List<StatementKind>>> statementWriteFiled =gaInfo.getMethodRelations().getRelations().getStatementWriteFiled();
		 for(Class<?> clazz:statementWriteFiled.keySet()){
			 Map<String, List<StatementKind>> sWriteFields = statementWriteFiled.get(clazz);
			 for(String st:sWriteFields.keySet())
				 statementDependences.put(clazz, sWriteFields.get(st));	
		 }
		 return statementDependences;
	}
	private  Map<Class<?>,  List<StatementKind>> Condition_isField(Data v1,String clz,String method){
		Map<Class<?>,  List<StatementKind>> statementDependences =new LinkedHashMap<Class<?>, List<StatementKind>>();
		 Map<Class<?>, Map<String, List<StatementKind>>> statementWriteFiled =gaInfo.getMethodRelations().getRelations().getStatementWriteFiled();
		 for(Class<?> clazz:statementWriteFiled.keySet()){
			 Map<String, List<StatementKind>> sWriteFields = statementWriteFiled.get(clazz);
			 if(sWriteFields.containsKey(v1.getFieldName()))
				 statementDependences.put(clazz, sWriteFields.get(v1.getFieldName()));				 
		 }

		
		 return statementDependences;

	}
	public  Map<Class<?>,  List<StatementKind>> Condition_private_method(String clz,String method){
		Map<Class<?>,  List<StatementKind>> statementDependences =new LinkedHashMap<Class<?>, List<StatementKind>>();

		 Map<Class<?>, Map<String, List<StatementKind>>> statementPrivateCall =gaInfo.getMethodRelations().getRelations().getPrivateDependences();
		 for(Class<?> clazz:statementPrivateCall.keySet()){
			 Map<String, List<StatementKind>> sCallPrivate = statementPrivateCall.get(clazz);
			 for(String st:sCallPrivate.keySet())
				 if(st.toString().equals(method))
					 	statementDependences.put(clazz, sCallPrivate.get(st));
		 }
		 //Add all methods that write in the public methods which call the private methods conatins the Target Branch
		 Map<Class<?>, Map<StatementKind, List<StatementKind>>> methodsDependence =gaInfo.getMethodRelations().getRelations().getStatementDependences();
		 for(Class<?> clazz:statementDependences.keySet()){
			  List<StatementKind> sWriteMethods= statementDependences.get(clazz);
//				  System.out.println(sWriteMethods);

			  Map<StatementKind, List<StatementKind>> sWriteMethodsCallPrivate= methodsDependence.get(clazz);
			 for(StatementKind st:sWriteMethodsCallPrivate.keySet()){
//				 System.out.println(st.toString());
				 if(sWriteMethods.contains(st)){
					 List<StatementKind> wMethodsList=sWriteMethodsCallPrivate.get(st);
					 for(StatementKind st_:wMethodsList)
						 if(!sWriteMethods.contains(st_))
								 sWriteMethods.add(st_);
				 }
			 }

		 }
		 return statementDependences;
	}
	public  Map<Class<?>,  List<StatementKind>>  Condition_public_method_ParamPredicate(String clzMethodTrgt,String method){
		Map<Class<?>,  List<StatementKind>> statementDependences =new LinkedHashMap<Class<?>, List<StatementKind>>();
			CUT cut=gaInfo.getTestCluster().getCluster().get(clzMethodTrgt);	
			Class<?> clazzMethodTarget=gaInfo.getTestCluster().getClassToTest().get(clzMethodTrgt);
		 
			StatementKind[]  st=cut.getMethods();
			for(StatementKind s:st){
				if(s.toString().equals(method)){
					List<StatementKind> list=new LinkedList<StatementKind>();
					if(statementDependences.get(clazzMethodTarget)!=null)
						list.addAll(statementDependences.get(clazzMethodTarget));
					if(!list.contains(s))
						list.add(s);
					statementDependences.put(clazzMethodTarget, list);
					break;
				}
			}
 
		 
	return	 statementDependences;
	}
	public  Map<Class<?>,  List<StatementKind>>  Condition_public_method(String clzMethodTrgt,String method){
		Map<Class<?>,  List<StatementKind>> statementDependences =new LinkedHashMap<Class<?>, List<StatementKind>>();
		 Map<Class<?>, Map<StatementKind, List<StatementKind>>> statementDependence =gaInfo.getMethodRelations().getRelations().getStatementDependences();
		 for(Class<?> clazz:statementDependence.keySet()){
			 Map<StatementKind, List<StatementKind>> sWriteMethods= statementDependence.get(clazz);
			 for(StatementKind st:sWriteMethods.keySet()){
//					 System.err.println("getRecommendMethods  st.toString() " + st.toString() + " c.getMName() "+ c.getMName()) ;
				 if(st.toString().equals(method)&& ((MethodDeclared) st).getMethod().getDeclaringClass().toString().contains(clzMethodTrgt)){
					List<StatementKind> list=statementDependences.get(method);
					if(list==null)
						list=new LinkedList<StatementKind>();
					list.addAll( sWriteMethods.get(st));
					HashSet<StatementKind> hashSet = new HashSet<StatementKind>(list);
					list=new LinkedList<StatementKind>(hashSet); 
					 	statementDependences.put(clazz, list);
				 }
			 }
		 }
			CUT cut=gaInfo.getTestCluster().getCluster().get(clzMethodTrgt);	
			Class<?> clazzMethodTarget=gaInfo.getTestCluster().getClassToTest().get(clzMethodTrgt);

		 //Constructors
		 Map<Class<?>,  List<StatementKind>> consDependence =gaInfo.getMethodRelations().getRelations().getConstructorDependences();
		 List<StatementKind> constructors= consDependence.get(clazzMethodTarget);
		 if(method.contains("<init>")){
			 for(StatementKind s:constructors){
				 if(s instanceof model.ConstantDeclared) continue;
				 if(s.toString().substring(s.toString().indexOf("<init>")).equals(method.toString().substring(method.toString().indexOf("<init>")))){
						List<StatementKind> list=new LinkedList<StatementKind>();
						if(statementDependences.get(clazzMethodTarget)!=null)
							list.addAll(statementDependences.get(clazzMethodTarget));
						if(!list.contains(s))
							list.add(s);
						statementDependences.put(clazzMethodTarget, list);
						break;
					}
				 }
		 }
		 
			StatementKind[]  st=cut.getMethods();
			for(StatementKind s:st){
				if(s.toString().equals(method)){
					List<StatementKind> list=new LinkedList<StatementKind>();
					if(statementDependences.get(clazzMethodTarget)!=null)
						list.addAll(statementDependences.get(clazzMethodTarget));
					if(!list.contains(s))
						list.add(s);
					statementDependences.put(clazzMethodTarget, list);
					break;
				}
			}
 
		 
	return	 statementDependences;
	}
	private   Value getDataCondition(Integer trgtBR,Condition c,int i){
//		if( trgtBR== 81)
//			System.out.println("RecommendGenerator.getDataCondition");
			Value v = c.getV1();
			if(i==2){
				DataUse duse=c.getUse1();
				if(duse!=null){
					DataDef d=duse.getDefInBlock();
					if(d!=null){
					Value def=(Value)d.getValue();
					if(def instanceof Virtualinvoke){
						 if(((Virtualinvoke)def).getArgs().size()!=0)
							return  ((Virtualinvoke)def).getArgs().get(0);
					}
					}
				}

				v=c.getV2();
				 if(c instanceof ConditionSwitch){
						Map<Integer,Integer> cases=((ConditionSwitch)c).getCases();
						if(cases.containsKey(trgtBR))
								v=new Constant(cases.get(trgtBR));
						else
							v=new Constant(((ConditionSwitch)c).getDefaultCase());
				 }
			}
			if(v instanceof Constant|| v instanceof ConstantString)
				return  v;
			if(v != null) {
				Data data = (Data) v;
				if(data.isField()||data.isParam())
					return data;
				else{
					
					DataUse duse=c.getUse1();
					if(i==2)
						duse=c.getUse2();
					if(duse!=null){
						DataDef d=duse.getDefInBlock();
						if(d==null) return v;
						Value def=(Value)d.getValue();
						if(def instanceof Constant)
							return v;
						if(def instanceof Virtualinvoke){
							return ((Virtualinvoke)def);
						}else if(def instanceof ValueArray){
							if(((ValueArray) def).getD().isField()|| ((ValueArray) def).getD().isParam())
								return ((ValueArray) def);
						}else
							return def;
//						
					}
					
				}
			}
			else if(v instanceof Constant){ //if('a'==c)

			}
		return  v;
	}
	@Override
	public String toString(){
		StringBuilder sb = new StringBuilder();
		//Set<Condition> branches=condition.values();
		sb.append("=========================================================\n");
		sb.append("\n");
		 TreeMap<Integer,Condition> sorted_map = new TreeMap<Integer,Condition>(condition);
	     for (Map.Entry<Integer, Condition> entry : sorted_map.entrySet()) {

				int brId=entry.getKey();
 				sb.append("[ "+ brId + " ] ");
				sb.append(branchMethod.get(brId).getFullQualifiedName());
   				sb.append(this.branchWithData.get(brId));
   	  			sb.append("\n");   			

   				sb.append(branchRelStatementList.get(brId)==null?" ":" "+branchRelStatementList.get(brId));
   				sb.append("\n");
//   				System.out.println(sb);
 		}
		return sb.toString();
	}
	
public  static void main(String[] args) {
	 GAInfo     gaInfo     = new GAInfo();
		ReadFilewgzMDR readFileWGZ = new ReadFilewgzMDR();
		try {
			Set<Class<?>> allClasses = new LinkedHashSet<Class<?>>();
			// get class from file
			allClasses.addAll(TestClusterBuilder.readClassFromFile());

			System.out.println("There are " + allClasses.size()
					+ " classes as input");

			System.out.println(allClasses);
			List<TestClusterBuilder> TestClusterList = TestClusterBuilder
					.buildTestClusterList(allClasses);
			 Map<String, BlockClass> blockClasses=readFileWGZ.readFileWGZ(TestClusterList);
			 Map<String,Set<Integer>> classBranches=readFileWGZ.getClazzCondition(blockClasses);
			readFileWGZ.displayBlock(blockClasses);
			gaInfo.setMapBlockClasses(blockClasses);
			gaInfo.setClazzBranches(classBranches);

			for (TestClusterBuilder tCluster : TestClusterList) {
			 System.out.println( "Building up method dependence model for diversifying...");
			 gaInfo.setTestCluster(tCluster);
			 Set<Class<?>> cluster = new LinkedHashSet<Class<?>>();
				for(String cName:tCluster.getCluster().keySet()){
					Class<?> cls=tCluster.getCluster().get(cName).getClazz();
					if(Filter.isPrimitiveOrStringType(cls))continue;
					if(cls.getName().startsWith("java.")) continue;
					if(cls.getName().startsWith("sun.")) continue;
					if(cls.getName().contains("$")) continue;
					if(!Filter.canUse(cls))continue;
					cluster.add(cls);
				}

				MethodDependences recommender = new MethodDependences(cluster);
				 recommender.buildDependence(tCluster.getStatements());
				 System.out.println("Finishing method dependence model building");
				 gaInfo.setMethodRelations(recommender);
				 
				 ////print out the relation methods dependency
					Set<Class<?>> classToTest= new LinkedHashSet<Class<?>>();

					for(Class<?> cls: tCluster.getClassToTest().values())
						classToTest.add(cls);
				System.out.println("--------------------");
				System.out.println("     CUT: "+ classToTest);
				System.out.println("--------------------");
//				System.out.println(recommender.showDependence(classToTest));
//				System.out.println(recommender.showStatmentsWriteFileds(classToTest));
				CopyOfRecommendGenerator11_18_2014 recommendGenerator=new  CopyOfRecommendGenerator11_18_2014(gaInfo);
				 System.out.println(recommendGenerator.toString());
			}
			
		} catch (Throwable e) {
			System.err.println("IO error: " + e.getMessage());
			System.exit(1);
		}
		System.exit(0);

}

}